from google.appengine.ext import webapp
from google.appengine.api import taskqueue
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
from google.appengine.api import mail
from datetime import datetime
from email.header import decode_header
from traceback import format_exc
from google.appengine.api import memcache

from common.entities import Attachment, AttachmentData, AttachmentTag
from common.utils import CachedMail


import logging as log

class NoAttachmentError(Exception): pass
class AttachmentTooBigError(Exception): pass

def setattrs(obj, **kwds):
    for k,v in kwds.iteritems(): setattr(obj, k, v)


def unormalize(s):
    return normalize('NFKD', s).encode('ascii', 'ignore').upper()

def send_info(to, status, subject, file_name):
    info_template = """Hello!

I'm writing to You to say You successfully %s attachment. Huray!
You can reach it using this direct link address:

http://archiwum.springfield.pl/serve/%s

Or you can find it on main page:

http://archiwum.springfield.pl

It was saved under file name: %s

"""
    log.info('sending info to %s' % to)
    mail.send_mail(sender="Simple mail archiver <sa@archive-mail.appspotmail.com>",
                   to=to,
                   subject="[Simple archiver info] %s" % subject,
                   body=info_template % (status, file_name, file_name))

def send_error(to, details, subject=''):
    error_template = """Hello!

Unfortunatelly something went wrong. I was unable to save your attachement.
Error details follow:

%s
"""
    log.info('sending error to %s' % to)
    mail.send_mail(sender="Simple mail archiver <sa@archive-mail.appspotmail.com>",
                   to=to,
                   subject="[Simple archiver info] %s" % subject,
                   body=error_template % (details))

def time():
    tmp = datetime.now()
    return tmp.replace(hour=tmp.hour+2)

class MailHandler(webapp.RequestHandler):
    def post(self):
        msg = None
        try:
            msg = CachedMail(mail.InboundEmailMessage(self.request.body))

            if msg.file_name == None: raise NoAttachmentError('Attachment not found in email!')
            if msg.attachment_to_big(): raise AttachmentTooBigError('I can handle only about 2.5MB attachments')

            log.info('Message TO: %s, Subject: %s' % (msg.to, msg.subject))
            log.info('Attachment name: %s, size: %s' % (msg.file_name, msg.attachment_size))

            memcache.set(msg.file_name, msg, 3600)
            taskqueue.add(url='/task/delete_attachment', params={'file_name':msg.file_name})

        except Exception, e:
            log.error(format_exc())
            send_error('filip.felicki@gmail.com', format_exc(), "%s: %s" % (e.__class__.__name__, str(e)))
            if msg and hasattr(msg, 'sender') and msg.sender != 'filip.felicki@gmail.com':
                send_error(msg.sender, format_exc(), "%s: %s" % (e.__class__.__name__, str(e)))

class DeleteAttachmentTask(webapp.RequestHandler):
    def post(self):
        file_name = self.request.get('file_name')
        at = Attachment.all().filter('file_name =', file_name).get()

        def txn(key):
            for child in AttachmentData.all().ancestor(key): child.delete()

        if at: 
            db.run_in_transaction(txn, at.key())
            taskqueue.add(url='/task/save_attachment', params={'file_name':file_name, 'key':at.key()})
        else:
            taskqueue.add(url='/task/save_attachment', params={'file_name':file_name})


class SaveAttachmentTask(webapp.RequestHandler):
    def post(self):
        msg = memcache.get(self.request.get('file_name'))
        key = self.request.get('key')

        if key: 
            at = Attachment.get(key)
            setattrs(at, saved_parts=0, update_time=time(), file_name=msg.file_name, sender=msg.sender, to=msg.to, domain=msg.domain, subject=msg.subject, parts=msg.parts)
        else:
            at = Attachment(saved_parts=0, update_time=time(), file_name=msg.file_name, sender=msg.sender, to=msg.to, domain=msg.domain, subject=msg.subject, parts=msg.parts)

        at.put()

        self.add_tasks(msg, at)

    def add_tasks(self, msg, attachment):
        for i in range(1, attachment.parts+1):
            taskqueue.add(url='/task/save_attachment_data', params={'key':attachment.key(), 
                                                                    'sender':attachment.sender, 
                                                                    'file_name':attachment.file_name, 
                                                                    'cnt':i, 
                                                                    'total':attachment.parts, 
                                                                    'encoding':msg.encoding})

        taskqueue.add(url='/task/save_tags', params={'tags':msg.tags, 'file_name':attachment.file_name})

class SaveAttachmentDataTask(webapp.RequestHandler):
    def post(self):
        key, sender, file_name, cnt, total, encoding = [self.request.get(x) for x in 'key', 'sender', 'file_name', 'cnt', 'total', 'encoding'] 
        cnt, total = int(cnt), int(total)
        data = memcache.get(file_name + '_%s'%cnt)
        self.save_attachment_data(sender, file_name, data, cnt, total, encoding, Attachment.get(key))

    def save_attachment_data(self, sender, file_name, data, cnt, total, encoding, parent):
        log.debug('saving attachment data [%s] chunk %s of %s' % (file_name, cnt, total))

        data = data.decode(encoding)

        def txn(key):
            parent = Attachment.get(key)
            AttachmentData(file_name=file_name, data=data, order=cnt, total=total, size=len(data), parent=parent).put()
            parent.saved_parts += 1
            parent.put()

            if parent.parts == parent.saved_parts:
                send_info(sender, 'Saved', file_name, file_name)

        db.run_in_transaction(txn, parent.key())

        log.debug('saving data finished')

class SaveTagsTask(webapp.RequestHandler):
    def post(self):
        file_name = self.request.get('file_name')
        tags = self.request.get('tags')

        if tags == '': return

        for tag in tags.split(','):
            at = AttachmentTag.all().filter('tag_name =', tag).get()
            if not at: at = AttachmentTag(tag_name=tag, file_list=[])

            if file_name not in at.file_list: 
                at.file_list.append(file_name)
                at.put()

def __main__():
    application = webapp.WSGIApplication([
                                          ('/_ah/mail/.*', MailHandler),
                                          ('/task/save_tags', SaveTagsTask),
                                          ('/task/delete_attachment', DeleteAttachmentTask),
                                          ('/task/save_attachment', SaveAttachmentTask),
                                          ('/task/save_attachment_data', SaveAttachmentDataTask)],
                                         debug=True)
    run_wsgi_app(application)

if __name__ == '__main__':
    __main__()
